package cn.wolfcode.service.impl;

import cn.wolfcode.common.exception.BusinessException;
import cn.wolfcode.common.web.CommonCodeMsg;
import cn.wolfcode.common.web.Result;
import cn.wolfcode.domain.*;
import cn.wolfcode.mapper.OrderInfoMapper;
import cn.wolfcode.mapper.PayLogMapper;
import cn.wolfcode.mapper.RefundLogMapper;
import cn.wolfcode.mq.MQConstant;
import cn.wolfcode.mq.OrderMessage;
import cn.wolfcode.redis.SeckillRedisKey;
import cn.wolfcode.service.IOrderInfoService;
import cn.wolfcode.service.ISeckillProductService;
import cn.wolfcode.util.IdGenerateUtil;
import cn.wolfcode.web.feign.IntegralFeignApi;
import cn.wolfcode.web.feign.PayFeignApi;
import cn.wolfcode.web.msg.SeckillCodeMsg;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.SneakyThrows;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by wolfcode-lanxw
 */
@Service
public class OrderInfoSeviceImpl implements IOrderInfoService {
    @Autowired
    @Lazy
    private IOrderInfoService  orderInfoService;
    @Autowired
    private ISeckillProductService seckillProductService;
    @Autowired
    private OrderInfoMapper orderInfoMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    @Autowired
    private PayLogMapper payLogMapper;
    @Autowired
    private RefundLogMapper refundLogMapper;
    public static ConcurrentHashMap<Long,Boolean> stockCountOverFlagMap = new ConcurrentHashMap<>();
    @Autowired
    private PayFeignApi payFeignApi;
    @Autowired
    private IntegralFeignApi intergralFeignApi;
    @Value("${pay.notifyUrl}")
    private String notifyUrl;
    @Value("${pay.returnUrl}")
    private String returnUrl;

    @Override
    //@Transactional
    public void doSeckill(Long seckillId, String phone, String token) {
        //  判断本地标识
        Boolean stockCountOverFlag = stockCountOverFlagMap.get(seckillId);
        if(stockCountOverFlag!=null && stockCountOverFlag){
            throw new BusinessException(SeckillCodeMsg.SECKILL_STOCK_OVER);
        }
        //SeckillProduct seckillProduct = seckillProductService.find(seckillId);
        //    一个用户只能抢购该场次一个的秒杀商品
        String orderSetKey = SeckillRedisKey.SECKILL_ORDER_SET.getRealKey(seckillId.toString());
        if(redisTemplate.opsForSet().isMember(orderSetKey,phone)){
            //  判断Redis中是否已经存在该用户
            throw new BusinessException(SeckillCodeMsg.REPEAT_SECKILL);
        }
        //    不要出现超卖的情况，不要出现少卖的情况
        String stockCountKey = SeckillRedisKey.SECKILL_PRODUCT_STOCK.getRealKey(seckillId.toString());
        Long remainCount = redisTemplate.opsForValue().decrement(stockCountKey);
        // 扣减库存
        if(remainCount<0){
            //     商品已经抢购完了
            //     保持Redis的库存为0，用户后续异常的情况下进行回滚操作
            //     存在问题:Redis递减完之后，进行Redis库存+1操作，保证Redis库存维持0的位置,每次都需要访问两次Redis
            redisTemplate.opsForValue().increment(stockCountKey);
            stockCountOverFlagMap.put(seckillId,true);
            throw new BusinessException(SeckillCodeMsg.SECKILL_STOCK_OVER);
        }
        //  发送消息
        OrderMessage msg = new OrderMessage();
        msg.setSeckillId(seckillId);
        msg.setUserPhone(phone);
        msg.setToken(token);
        SendResult result = rocketMQTemplate.syncSend(MQConstant.ORDER_CREATE_TOPIC, msg);
        if(!SendStatus.SEND_OK.equals(result.getSendStatus())){
            redisTemplate.opsForValue().increment(stockCountKey);
            throw new BusinessException(SeckillCodeMsg.SECKILL_ERROR);
        }
        /*int effectCount = seckillProductService.decrStockCount(seckillId);
        if (effectCount==0){
            throw new BusinessException(SeckillCodeMsg.SECKILL_STOCK_OVER);
        }
        //  创建秒杀订单
        String orderNo = null;
        try {
            orderNo = createOrder(seckillId, phone);
        } catch (Exception e) {
            //  创建订单的时候出现异常
            redisTemplate.opsForValue().increment(stockCountKey);
            throw e;
        }*/
    }

    @Override
    public OrderInfo find(String orderNo) {
        return orderInfoMapper.find(orderNo);
    }

    @Override
    @Transactional
    public String createSeckillOrder(Long seckillId, String userPhone) {
        int effectCount = seckillProductService.decrStockCount(seckillId);
        if (effectCount==0){
            throw new BusinessException(SeckillCodeMsg.SECKILL_STOCK_OVER);
        }
        //  创建秒杀订单
        String orderNo = null;
        try {
            orderNo = createOrder(seckillId, userPhone);
        } catch (Exception e) {
            String stockCountKey = SeckillRedisKey.SECKILL_PRODUCT_STOCK.getRealKey(seckillId.toString());
            //  创建订单的时候出现异常 , 为本地标识设置为false
            redisTemplate.opsForValue().increment(stockCountKey);
            //     发送消息到MQ中
            rocketMQTemplate.syncSend(MQConstant.STOCK_OVER_FLAG_TOPIC,seckillId);
            //OrderInfoSeviceImpl.stockCountOverFlagMap.put(seckillId,false);
            throw e;
        }
        return orderNo;
    }

    @Override
    @Transactional
    public void orderPayTimeoutCheck(String orderNo) {
        OrderInfo orderInfo = orderInfoMapper.find(orderNo);
        if(OrderInfo.STATUS_UNPAID.equals(orderInfo.getStatus())){
            //    订单未支付
            //   修改订单状态
            int effectCount = orderInfoMapper.changeOrderStatus(orderNo, OrderInfo.STATUS_TIMEOUT, OrderInfo.STATUS_UNPAID);
            if(effectCount==0){
                //   该订单已经被其他线程修改了
                return;
            }
            //  增加数据库的库存
            seckillProductService.incrStockCount(orderInfo.getSeckillId());
        }
    }


    @Override
    public String pay(String orderNo) {
        OrderInfo  orderInfo = orderInfoMapper.find(orderNo);
        if(orderInfo==null){
            throw new BusinessException(CommonCodeMsg.ILLEGAL_OPERATION);
        }
        if(!OrderInfo.STATUS_UNPAID.equals(orderInfo.getStatus())){
            return "";
        }
        //  调用支付宝接口
        PayVo vo = new PayVo();
        vo.setOutTradeNo(orderNo);
        vo.setTotalAmount(orderInfo.getSeckillPrice()+""); //   支付金额
        vo.setSubject(orderInfo.getProductName()); //    订单名称
        vo.setBody(orderInfo.getProductName()); //   订单描述
        vo.setReturnUrl(returnUrl); //   同步回调地址
        vo.setNotifyUrl(notifyUrl); //   异步回调
        Result<String> result = payFeignApi.pay(vo);
        /*
        *    1.   result == null 说明支付服务不可用，走降级方法
        *    2.   result.getCode() != 0 说明支付服务不可用，但是调用内部报错了，统一异常处理返回Result(code=500xx)
        *    3.   result!=null,code ==200  说明支付服务可用，返回支付宝的html
        * */
        if(result==null || result.hasError()){
            throw new BusinessException(SeckillCodeMsg.PAY_SERVER_ERROR);
        }
        String html = result.getData();
        return html;
    }

    @Override
    public void orderNotify(Map<String, String> params) {
        //  验签操作，
        Result<Boolean> result = payFeignApi.rsaCheckV1(params);
        if(result==null || result.hasError() || !result.getData()){
            throw new BusinessException(SeckillCodeMsg.PAY_SERVER_ERROR);
        }
        //  验签成功，执行业务逻辑
        String orderNo = params.get("out_trade_no");
        //  把订单修改为已支付状态
        int effectCount = orderInfoMapper.changeOrderToPayStatus(orderNo,OrderInfo.STATUS_ACCOUNT_PAID,OrderInfo.STATUS_UNPAID,OrderInfo.PAYTYPE_ONLINE);
        if(effectCount == 0){
            //    说明支付回调的时候，其他线程已经修改了订单状态，走退款的逻辑
            //    插入数据表中，客服审核通过之后，才对该订单进行退款的操作
            return;
        }
        //  其他逻辑 通知仓库出库，短信通知
    }


    @Override
    public String orderReturn(Map<String, String> params) {
        //  验签操作，
        Result<Boolean> result = payFeignApi.rsaCheckV1(params);
        if(result==null || result.hasError() || !result.getData()){
            throw new BusinessException(SeckillCodeMsg.PAY_SERVER_ERROR);
        }
        //  验签成功，执行业务逻辑
        String orderNo = params.get("out_trade_no");
        return orderNo;
    }

    /**
     * 退款
     * @param orderNo
     */
    @Override
    public void refund(String orderNo) {
        OrderInfo orderInfo = orderInfoMapper.find(orderNo);
        if(orderInfo==null){
            throw new BusinessException(CommonCodeMsg.ILLEGAL_OPERATION);
        }
        if(!OrderInfo.STATUS_ACCOUNT_PAID.equals(orderInfo.getStatus())){
            return;
        }
        if(OrderInfo.PAYTYPE_ONLINE.equals(orderInfo.getPayType())){
            //    在线支付
            this.refundOnline(orderInfo);
        }
        else{
            //    积分支付
            orderInfoService.refundByIntergral(orderInfo);
        }
    }

    /**
     * 积分支付 退款
     * @param orderInfo
     */
    @SneakyThrows
    @GlobalTransactional
    public void refundByIntergral(OrderInfo orderInfo) {
        //  插入退款记录
        RefundLog log = new RefundLog();
        log.setOrderNo(orderInfo.getOrderNo()); //  订单编号
        log.setRefundAmount(orderInfo.getIntegral());//  退款金额
        log.setRefundReason("不想要了");//  退款原因
        log.setRefundTime(new Date());
        try {
            refundLogMapper.insert(log);
        }catch (Exception e){
            return;
        }
        //  远程调用积分服务，给指定用户增加积分
        OperateIntegralVo vo = new OperateIntegralVo();
        vo.setPhone(orderInfo.getPhone());
        vo.setValue(orderInfo.getIntegral());
        Result<String> result = intergralFeignApi.refund(vo);
        if(result==null || result.hasError()){
            throw new BusinessException(SeckillCodeMsg.INTEGRAL_SERVER_ERROR);
        }
        //  修改订单状态为退款
        int effectCount = orderInfoMapper.changeOrderStatus(orderInfo.getOrderNo(), OrderInfo.STATUS_REFUND, OrderInfo.STATUS_ACCOUNT_PAID);
        if(effectCount==0){
            //   订单状态被其他线程修改了  人工处理
            return;
        }
    }

    /**
     * 积分支付
     * @param orderNo
     */
    @Override
    @GlobalTransactional
    public void payByIntegral(String orderNo) {
        OrderInfo orderInfo = orderInfoMapper.find(orderNo);
        if(orderInfo==null){
            throw new BusinessException(CommonCodeMsg.ILLEGAL_OPERATION);
        }
        if(!OrderInfo.STATUS_UNPAID.equals(orderInfo.getStatus())){
            return;
        }
        //  插入支付日志(保证幂等性)
        PayLog log = new PayLog();
        log.setOrderNo(orderNo); //   订单编号
        log.setPayTime(new Date()); //  支付时间
        log.setTotalAmount(orderInfo.getIntegral());//  积分数量
        try {
            payLogMapper.insert(log);
        }catch (Exception e){
            return;
        }
        //  调用积分服务，实现积分扣减
        OperateIntegralVo vo = new OperateIntegralVo();
        vo.setPhone(orderInfo.getPhone()); // 用户
        vo.setValue(orderInfo.getIntegral()); // 积分数量
        Result<Boolean> result = intergralFeignApi.pay(vo);
        if(result==null || result.hasError()){
            throw new BusinessException(SeckillCodeMsg.INTEGRAL_SERVER_ERROR);
        }
        if(!result.getData()){
            throw new BusinessException(SeckillCodeMsg.INTEGRAL_NOT_ENOUGH);
        }
        //   修改订单状态
        int effectCount = orderInfoMapper.changeOrderToPayStatus(orderNo, OrderInfo.STATUS_ACCOUNT_PAID, OrderInfo.STATUS_UNPAID, OrderInfo.PAYTYPE_INTEGRAL);
        if(effectCount == 0){
            //    说明支付回调的时候，其他线程已经修改了订单状态，走退款的逻辑
            //    插入数据表中，客服审核通过之后，才对该订单进行退款的操作
            throw new BusinessException(SeckillCodeMsg.ORDER_STATUS_CHANGE);
        }
    }


    public void refundOnline(OrderInfo orderInfo) {
        //  调用支付退款接口 修改订单状态
        RefundVo vo = new RefundVo();
        vo.setOutTradeNo(orderInfo.getOrderNo());
        vo.setRefundAmount(orderInfo.getSeckillPrice().toString()); // 退款金额与支付金额一致
        vo.setRefundReason("不想要了");
        Result<Boolean> result = payFeignApi.refund(vo);
        if(result==null || result.hasError() || !result.getData()){
            throw new BusinessException(SeckillCodeMsg.PAY_SERVER_ERROR);
        }
        int effectCount = orderInfoMapper.changeOrderStatus(orderInfo.getOrderNo(), OrderInfo.STATUS_REFUND, OrderInfo.STATUS_ACCOUNT_PAID);
        if(effectCount == 0){
            //    说明支付回调的时候，其他线程已经修改了订单状态，走退款的逻辑
            //    插入数据表中，客服审核通过之后，才对该订单进行退款的操作
            return;
        }
    }


    public String createOrder(Long seckillId, String phone) {
        SeckillProduct vo = seckillProductService.find(seckillId);
        OrderInfo orderInfo = new OrderInfo();
        BeanUtils.copyProperties(vo,orderInfo);
        orderInfo.setOrderNo(IdGenerateUtil.get().nextId()+""); // 订单号
        orderInfo.setPhone(phone);  // 用户id
        orderInfo.setCreateDate(new Date()); // 创建时间
        orderInfo.setSeckillDate(vo.getStartDate()); // 秒杀日期
        orderInfo.setSeckillTime(vo.getTime()); // 秒杀场次
        orderInfo.setSeckillId(vo.getId()); // 秒杀的id
        orderInfoMapper.insert(orderInfo);
        return orderInfo.getOrderNo();
    }


}
